package clang.ir;

import fixedpoint.DataflowException;
import clang.Datatype;
import java.util.ArrayList;

public abstract class Node {

    public static enum StorageClass {

        EXTERNAL {

            @Override
            public String toString() {
                return "external";
            }
        },
        INTERNAL_UNDECLARED {

            @Override
            public String toString() {
                return "internal undeclared";
            }
        },
        INTERNAL_DECLARED {

            @Override
            public String toString() {
                return "internal declared";
            }
        };

        @Override
        public abstract String toString();
    }
    protected StorageClass storageClass;
    protected Datatype datatype;
    protected String description;
    protected int evaluationIndex;
    protected final static int INVALID_EVALUATION_INDEX = -1;

    public Node() {
        description = "";
        storageClass = StorageClass.INTERNAL_UNDECLARED;
        evaluationIndex = INVALID_EVALUATION_INDEX;
    }

    public void setStorageClass(StorageClass storageClass) {
        this.storageClass = storageClass;
    }

    public StorageClass getStorageClass() {
        return storageClass;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public void setDatatype(Datatype datatype) {
        this.datatype = datatype;
    }

    public Datatype getDatatype() {
        return datatype;
    }

    public int getEvaluationIndex() {
        return evaluationIndex;
    }

    public void setEvaluationIndex(int evaluationIndex) {
        this.evaluationIndex = evaluationIndex;
    }

    public abstract void buildEvaluationIndex(Node parent);

    private boolean isValidCLangIdentifier(String s) {
        if (!s.isEmpty()) {
            char c = s.charAt(0);
            if (Character.isLetter(c) || (c == '_')) {
                for (int i = 1; i < s.length(); i++) {
                    c = s.charAt(i);
                    if (!(Character.isLetter(c) || Character.isDigit(c) || (c == '_'))) {
                        return false;
                    }
                }
                return true;
            }
        }
        return false;
    }

    public String getCLangName() {
        if (storageClass == StorageClass.EXTERNAL) {
            return description;
        } else {
            return getIdentifier();
        }
    }

    public String getIdentifier() {
        return "node" + this.hashCode();
    }

    /**
     * @precondition Dependencies are resolved.
     */
    public String getRuntimeValue() throws DataflowException {
        switch (storageClass) {
            case EXTERNAL:
                return getCLangName();
            case INTERNAL_DECLARED:
                return getCLangName();
            case INTERNAL_UNDECLARED:
                return getRuntimeExpression();
            default:
                throw new DataflowException("Unknown node storage class");
        }
    }

    public abstract String getRuntimeExpression() throws DataflowException;

    public String getRuntimeValueWithWrap() throws DataflowException {
        String s = getRuntimeValue();
        if (storageClass == StorageClass.INTERNAL_UNDECLARED) {
            if (runtimeExpressionIsComposite()) {
                s = wrap(s);
            }
        }
        return s;
    }
    public static final String NEWLINE = System.getProperty("line.separator");

    protected static String wrap(String s) {
        return "(" + s + ")";
    }

    protected static String toCLangIntegerLiteral(long value) {
        String s = Long.toString(value);
        if (value > Datatype.UNSIGNED_SHORT.getHigh() || value < Datatype.SIGNED_SHORT.getLow()) {
            s += "L";
        }
        return s;
    }

    public abstract boolean runtimeExpressionIsComposite();
}
